<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 聊天助手</title>
    <link rel="stylesheet" href="/css/style.css">
    <!-- 引入 Vue.js -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <!-- 引入 Axios 用于HTTP请求 -->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <!-- 替换 Marked.js 为 markdown-it -->
    <script src="https://cdn.jsdelivr.net/npm/markdown-it@12.3.2/dist/markdown-it.min.js"></script>
    <!-- 引入代码高亮 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/styles/github.css">
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/highlight.min.js"></script>
    <!-- 引入语言包以支持更多编程语言 -->
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/java.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/python.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/javascript.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/xml.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/css.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/json.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/bash.min.js"></script>
    <!-- 添加markdown-it插件 -->
    <script src="https://cdn.jsdelivr.net/npm/markdown-it-highlight/dist/index.min.js"></script>
    <style>
        /* 添加防抖动样式 */
        .message-text {
            min-height: 20px;
            transition: height 0.1s ease-out;
            will-change: contents;
            contain: content;
        }
        .message-text p {
            margin-top: 0.5em;
            margin-bottom: 0.5em;
        }
        pre {
            margin-top: 0.5em;
            margin-bottom: 0.5em;
            overflow-x: auto;
        }
        /* 预留代码块空间，减少抖动 */
        .ai-message .message-text {
            padding-bottom: 8px;
        }
        /* 平滑过渡效果 */
        .message-container {
            transition: opacity 0.2s;
        }
        .ai-message .message-content {
            transition: transform 0.1s ease-out;
        }
        /* 流式内容平滑更新 */
        @keyframes fadeIn {
            from { opacity: 0.6; }
            to { opacity: 1; }
        }
        .message-text code, .message-text pre {
            animation: fadeIn 0.3s ease-in-out;
        }
        /* 防止非预期滚动 */
        .chat-messages {
            scroll-behavior: smooth;
            scroll-padding-bottom: 20px;
        }
        /* 预渲染容器 - 用于避免抖动 */
        /*.pre-render {
            position: absolute;
            visibility: hidden;
            height: 0;
            overflow: hidden;
        }*/
    </style>
</head>
<body>
    <div id="app">
        <div class="container">
            <!-- 左侧边栏 -->
            <div class="sidebar">
                <div class="user-info">
                    <span>聊天DEMO</span>
                </div>
                <div class="new-chat-btn" @click="startNewChat">
                    <span>新建对话</span>
                </div>
                
                <!-- 最近会话列表 -->
                <div class="history-section">
                    <div class="section-title" v-if="recentChats.length > 0">本周</div>
                    <div class="chat-history-item" 
                         v-for="chat in recentChats" 
                         :key="chat.id"
                         @click="selectChat(chat)">
                        {{ chat.title }}
                    </div>
                    
                    <div class="section-title" v-if="olderChats.length > 0">本月</div>
                    <div class="chat-history-item" 
                         v-for="chat in olderChats" 
                         :key="chat.id"
                         @click="selectChat(chat)">
                        {{ chat.title }}
                    </div>
                </div>
            </div>
            
            <!-- 主聊天区域 -->
            <div class="main-area">
                <!-- 聊天消息区域 -->
                <div class="chat-messages" ref="chatMessages">
                    <div v-if="messages.length === 0" class="welcome-message">
                        <h1>Hi~ 我是一个智能机器人，精通各行业的知识。你可以问我任何问题。</h1>
                    </div>
                    <div v-for="(message, index) in messages" 
                         :key="index" 
                         :class="['message-container', message.role === 'user' ? 'user-message' : 'ai-message']">
                        <div class="message-content">
                            <div v-if="message.role === 'assistant'" class="message-header">
                                <span class="bot-name">机器人</span>
                            </div>
                            <div class="message-text" v-html="message.streaming ? formatStreamingMessage(message.content) : formatMessage(message.content, message.role)"></div>
                        </div>
                    </div>
                    <div v-if="isLoading" class="thinking">
                        <div class="dot-flashing"></div>
                    </div>
                </div>
                
                <!-- 输入区域 -->
                <div class="input-area">
                    <div class="input-container">
                        <textarea 
                            v-model="userInput" 
                            @keydown.enter.prevent="sendMessage"
                            placeholder="有问题，尽管问，shift+enter换行"
                            rows="1"
                            ref="inputField"></textarea>
                        <button 
                            class="send-button" 
                            @click="sendMessage" 
                            :disabled="isLoading || !userInput.trim()">
                            <svg v-if="!isLoading" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
                            </svg>
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref, onMounted, nextTick, computed, watch } = Vue;
        
        // 创建并配置markdown-it实例
        const md = window.markdownit({
            html: false,        // 禁用原始HTML以防XSS
            xhtmlOut: false,    // 使用'/'关闭单标签 (<br />)
            breaks: true,       // 将段落中的'\n'转换为<br>
            langPrefix: 'language-',  // 为代码块中的language标签添加前缀
            linkify: true,      // 将URL转换为链接
            typographer: true,  // 启用一些语言中立的替换和引号美化
            quotes: ["\"\"", "''"],   // 引号美化对
            highlight: function(str, lang) {
                if (lang && hljs.getLanguage(lang)) {
                    try {
                        return '<pre class="hljs"><code>' +
                               hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
                               '</code></pre>';
                    } catch (__) {}
                }
                return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
            }
        });
        
        createApp({
            setup() {
                const userInput = ref('');
                const messages = ref([]);
                const isLoading = ref(false);
                const chatMessages = ref(null);
                const inputField = ref(null);
                const recentChats = ref([]);
                const olderChats = ref([]);
                const currentSession = ref(null);
                
                // 加载最近的聊天历史
                const loadChatHistory = async () => {
                    try {
                        // 获取最近的会话
                        const recentResponse = await fetch('/api/chat/sessions/recent');
                        if (recentResponse.ok) {
                            recentChats.value = await recentResponse.json();
                            console.log('Recent chats loaded:', recentChats.value);
                        }
                        
                        // 获取更早的会话
                        const olderResponse = await fetch('/api/chat/sessions/older');
                        if (olderResponse.ok) {
                            olderChats.value = await olderResponse.json();
                            console.log('Older chats loaded:', olderChats.value);
                        }
                    } catch (error) {
                        console.error('加载聊天历史失败:', error);
                    }
                };
                
                // 更新会话标题
                const updateSessionTitle = async (sessionId, title) => {
                    try {
                        const response = await fetch(`/api/chat/sessions/${sessionId}/title`, {
                            method: 'PUT',
                            headers: {
                                'Content-Type': 'application/json'
                            },
                            body: JSON.stringify({ title })
                        });
                        
                        if (response.ok) {
                            console.log('Session title updated:', title);
                            // 更新当前会话的标题
                            if (currentSession.value && currentSession.value.id === sessionId) {
                                currentSession.value.title = title;
                            }
                            
                            // 刷新会话列表
                            await loadChatHistory();
                        } else {
                            console.error('Failed to update session title, status:', response.status);
                        }
                    } catch (error) {
                        console.error('更新会话标题失败:', error);
                    }
                };
                
                // 自动调整输入框高度
                const adjustTextareaHeight = () => {
                    const textarea = inputField.value;
                    if (!textarea) return;
                    
                    textarea.style.height = 'auto';
                    textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';
                };
                
                // 滚动到底部
                const scrollToBottom = async () => {
                    await nextTick();
                    if (chatMessages.value) {
                        chatMessages.value.scrollTop = chatMessages.value.scrollHeight;
                    }
                };
                
                // 创建安全的HTML转义函数
                const escapeHtml = (text) => {
                    return text
                        .replace(/&/g, '&amp;')
                        .replace(/</g, '&lt;')
                        .replace(/>/g, '&gt;')
                        .replace(/"/g, '&quot;')
                        .replace(/'/g, '&#039;');
                };
                
                // 使用简化的渲染方法处理流式内容
                const formatStreamingMessage = (content) => {
                    if (!content) return '';
                    
                    try {
                        // 直接使用markdown-it渲染，不做特殊处理
                        return md.render(content);
                    } catch (error) {
                        console.error('Streaming Markdown parsing error:', error);
                        // 如果渲染失败，回退到基本的安全显示
                        return escapeHtml(content).replace(/\n/g, '<br>');
                    }
                };
                
                // 发送消息
                const sendMessage = async () => {
                    const text = userInput.value.trim();
                    if (!text || isLoading.value) return;
                    
                    // 确保有一个活跃的会话
                    if (!currentSession.value) {
                        await startNewChat('新对话');
                        if (!currentSession.value) return; // 如果创建会话失败，则退出
                    }
                    
                    // 添加用户消息
                    messages.value.push({ role: 'user', content: text });
                    userInput.value = '';
                    adjustTextareaHeight();
                    scrollToBottom();
                    
                    isLoading.value = true;
                    
                    try {
                        // 创建一个空的AI回复占位
                        const aiMessageIndex = messages.value.length;
                        messages.value.push({ 
                            role: 'assistant', 
                            content: '',
                            streaming: true // 标记消息正在流式传输中
                        });
                        
                        // 初始滚动到底部，显示新消息的开始
                        scrollToBottom();
                        
                        // 记录用户是否在底部
                        let isAtBottom = true;
                        let lastUpdateTime = Date.now();
                        
                        // 监听滚动事件，检测用户是否手动滚动离开底部
                        const scrollHandler = () => {
                            if (!chatMessages.value) return;
                            
                            const { scrollTop, scrollHeight, clientHeight } = chatMessages.value;
                            // 如果距离底部很近（20px以内），认为用户在底部
                            isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
                        };
                        
                        // 添加滚动事件监听
                        if (chatMessages.value) {
                            chatMessages.value.addEventListener('scroll', scrollHandler);
                        }
                        
                        // 使用Fetch API进行流式请求
                        let requestUrl;
                        let requestOptions = {};
                        
                        if (currentSession.value) {
                            // 使用会话API
                            requestUrl = `/chat/stream/session/${currentSession.value.id}`;
                            requestOptions = {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json'
                                },
                                body: JSON.stringify({ 
                                    prompt: text
                                })
                            };
                        } else {
                            // 使用无会话API
                            requestUrl = `/chat/stream/${encodeURIComponent(text)}`;
                            requestOptions = { method: 'GET' };
                        }
                        
                        const response = await fetch(requestUrl, requestOptions);
                        const reader = response.body.getReader();
                        const decoder = new TextDecoder();
                        
                        let completeResponse = '';
                        let chunkCount = 0;
                        
                        // 简化的更新逻辑：节流更新以减少重绘
                        while (true) {
                            const { done, value } = await reader.read();
                            if (done) break;
                            
                            const chunk = decoder.decode(value, { stream: true });
                            completeResponse += chunk;
                            chunkCount++;
                            
                            const now = Date.now();
                            
                            // 对前5个块立即更新以保持响应性，之后每100ms更新一次
                            if (chunkCount <= 5 || now - lastUpdateTime > 100) {
                                messages.value[aiMessageIndex].content = completeResponse;
                                lastUpdateTime = now;
                                
                                // 仅当用户在底部时滚动
                                if (isAtBottom) {
                                    scrollToBottom();
                                }
                            }
                        }
                        
                        // 最终更新，确保显示完整响应
                        messages.value[aiMessageIndex].content = completeResponse;
                        
                        // 移除滚动事件监听
                        if (chatMessages.value) {
                            chatMessages.value.removeEventListener('scroll', scrollHandler);
                        }

                        // 完成响应后，标记消息不再流式传输
                        messages.value[aiMessageIndex].streaming = false;
                        
                        // 消息发送完成后，如果是新会话，更新会话标题
                        if (currentSession.value && currentSession.value.title === '新对话') {
                            // 使用AI回复的前20个字符作为会话标题
                            let newTitle = completeResponse.substring(0, 20);
                            // 移除可能的Markdown符号
                            newTitle = newTitle.replace(/[#*`_]/g, '').trim();
                            // 添加省略号
                            newTitle = newTitle + '...';
                            
                            // 更新会话标题
                            await updateSessionTitle(currentSession.value.id, newTitle);
                        }
                    } catch (error) {
                        console.error('Error:', error);
                        messages.value.push({ 
                            role: 'assistant', 
                            content: '抱歉，发生了错误，请稍后再试。'
                        });
                    } finally {
                        isLoading.value = false;
                        // 最后一次滚动到底部，确保完整的回复是可见的
                        scrollToBottom();
                    }
                };
                
                // 格式化消息内容（Markdown渲染）
                const formatMessage = (content, role) => {
                    if (!content) return '';
                    
                    // 用户消息只需要简单处理换行符
                    if (role === 'user') {
                        return content.replace(/\n/g, '<br>');
                    }
                    
                    // AI消息使用markdown-it渲染
                    try {
                        return md.render(content);
                    } catch (error) {
                        console.error('Markdown parsing error:', error);
                        // 如果渲染失败，使用更简单的方法来确保显示
                        return escapeHtml(content).replace(/\n/g, '<br>');
                    }
                };
                
                // 开始新对话
                const startNewChat = async (title = '新对话') => {
                    try {
                        console.log('Starting new chat with title:', title);
                        
                        // 先清空当前会话引用，避免历史数据干扰
                        currentSession.value = null;
                        
                        // 重置消息列表
                        messages.value = [];
                        
                        // 创建新会话
                        const response = await fetch('/api/chat/sessions', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json'
                            },
                            body: JSON.stringify({ title })
                        });
                        
                        if (response.ok) {
                            const data = await response.json();
                            currentSession.value = {
                                id: data.sessionId,
                                title
                            };
                            console.log('New session created:', currentSession.value);
                            
                            // 重新加载会话列表
                            await loadChatHistory();
                        } else {
                            console.error('Failed to create new session, status:', response.status);
                        }
                    } catch (error) {
                        console.error('创建新会话失败:', error);
                    }
                };
                
                // 选择历史对话
                const selectChat = async (chat) => {
                    try {
                        console.log('Selecting chat:', chat);
                        
                        // 先重置消息列表和当前会话
                        messages.value = [];
                        currentSession.value = null;
                        
                        // 然后设置新的当前会话
                        currentSession.value = chat;
                        
                        // 加载会话中的消息
                        const response = await fetch(`/api/chat/sessions/${chat.id}/messages`);
                        if (response.ok) {
                            const chatMessages = await response.json();
                            console.log('Chat messages loaded:', chatMessages.length);
                            
                            messages.value = chatMessages.map(msg => ({
                                role: msg.role,
                                content: msg.content
                            }));
                            
                            scrollToBottom();
                        } else {
                            console.error('Failed to load messages, status:', response.status);
                        }
                    } catch (error) {
                        console.error('加载会话消息失败:', error);
                    }
                };
                
                // 监听输入变化以调整高度
                watch(userInput, () => {
                    adjustTextareaHeight();
                });
                
                onMounted(() => {
                    // 加载聊天历史
                    loadChatHistory();
                    
                    // 初始化调整高度
                    adjustTextareaHeight();
                    
                    // 聚焦输入框
                    if (inputField.value) {
                        inputField.value.focus();
                    }
                });
                
                return {
                    userInput,
                    messages,
                    isLoading,
                    chatMessages,
                    inputField,
                    recentChats,
                    olderChats,
                    sendMessage,
                    formatMessage,
                    formatStreamingMessage,
                    startNewChat,
                    selectChat
                };
            }
        }).mount('#app');
    </script>
</body>
</html> 